import { domain } from '@/config'
import { createResolvablePromise } from './common'
import { showToast } from './toast'
import { getToken } from './token'
// import { fetch } from '@tauri-apps/plugin-http';

// const domain = import.meta.env.VITE_API_BASE_URL
/**
 * 接口地址前缀
 */
export const BASE_URL = domain
/**
 * 资源文件源地址前缀
 */
export const RESOURCES_URL = domain
type MethodType =
  | 'OPTIONS'
  | 'GET'
  | 'HEAD'
  | 'POST'
  | 'PUT'
  | 'DELETE'
  | 'TRACE'
  | 'CONNECT'
  | 'PATCH'

let nextTimeOut: number = null
export const setNextNetworkTimeOut = (time: number) => (nextTimeOut = time)
export const NetworkTimeOutError = Symbol()

const getFillUrl = (url: string) => {
  return url.indexOf('http://') == 0 || url.indexOf('https://') == 0
    ? url
    : BASE_URL + url
}
/**
 *
 * @param {string} method
 * @param {string} url
 * @param {object} data
 * @returns {Promise}
 */
const send = async <T = any>(
  method: MethodType,
  url: string,
  data: object | FormData | null = null,
  other: any = {}
): Promise<R<T>> => {
  const controller = new AbortController()
  const { signal } = controller
  const header: any = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Credentials': true,
  }
  let body: FormData | string | null = null
  if (data != null) {
    if (data instanceof FormData) {
      body = data
    } else {
      header['Content-Type'] = 'application/json;charset=UTF-8'
      body = JSON.stringify(data)
    }
  }
  if (method.toUpperCase() == 'GET' || method.toUpperCase() == 'HEAD') {
    body = null
  }
  if (typeof other['header'] == 'object') {
    for (let item in other['header']) {
      header[item] = other['header'][item]
    }
    other['header'] = undefined
  }
  const token = getToken()
  if (token && !header['token']) header['token'] = token
  let resultData = null
  let gresult = null
  let completed = false
  let timer = null
  let timeoutPromise: Promise<any> = null
  if (nextTimeOut !== null) {
    timeoutPromise = new Promise((_, reject) => {
      timer = setTimeout(() => {
        if (completed) return
        reject(NetworkTimeOutError)
        controller.abort()
      }, nextTimeOut)
    })
    nextTimeOut = null
  }
  try {
    const realUrl = getFillUrl(url)
    const task = [
      fetch(realUrl, {
        method,
        headers: header,
        body,
        signal,
      }),
    ]
    if (timeoutPromise) task.push(timeoutPromise)
    const result = await Promise.race(task)
    gresult = result
    // @ts-ignore
    if (result == NetworkTimeOutError) throw NetworkTimeOutError
    // console.log(body)

    resultData = await result.json()
  } catch (e: any) {
    if (e == NetworkTimeOutError) throw NetworkTimeOutError
    if (e.errMsg && e.errMsg.includes('fail timeout')) {
      showToast('网络异常')
      throw '网络异常'
    }
    console.log(e)
  } finally {
    completed = true
    timer && clearTimeout(timer)
  }
  if (resultData == null) {
    if (gresult.status == 204 && other['noResult']) {
      const r = new R<T>({
        code: 204,
        msg: '无数据',
        data: null,
      })
      return r
    }

    showToast('网络异常')
    return new R<T>({
      code: 500,
      msg: '网络异常',
    })
  }
  const r = new R<T>(resultData as any as R<T>)

  const stopHandle = {
    data: r,
    intercept: false,
  }
  if (stopHandle.intercept) {
    throw new Error('network handler intercept')
  }
  return r
}

export const getQueryUrl = (url: string, data: any = null): string => {
  let init = false
  for (let item in data) {
    if (
      typeof data[item] == 'boolean' ||
      typeof data[item] == 'number' ||
      typeof data[item] == 'string'
    ) {
      const prefix = init ? '&' : '?'
      init = true
      url += `${prefix}${item}=${data[item]}`
    } else if (typeof data[item] == 'object' && data[item] != null) {
      throw new Error('get或del请求不能出现object类型的参数')
    }
  }
  return url
}

/**
 * @returns
 */
const get = <T = any>(url: string, data: any = null, other: object = {}) => {
  if (data == null) {
    return send<T>('GET', url, null, other)
  }
  url = getQueryUrl(url, data)
  return send<T>('GET', url, null, other)
}
const del = <T>(url: string, data: any = null, other: object = {}) => {
  if (data == null) {
    return send<T>('DELETE', url, null, other)
  }
  url = getQueryUrl(url, data)
  return send<T>('DELETE', url, null, other)
}
/**
 *
 * @param {string} url
 * @param {object} data
 * @returns
 */
const post = <T>(url: string, data: any = {}, other: object = {}) =>
  send<T>('POST', url, data, other)
const put = <T>(url: string, data: any = {}, other: object = {}) =>
  send<T>('PUT', url, data, other)
const patch = <T>(url: string, data: any = {}, other: object = {}) =>
  send<T>('PATCH', url, data, other)
function fileUpload<T>(
  url: string,
  formdata: FormData,
  file?: null,
  onProgress?: (percent: number) => void
): Promise<R<T>>
function fileUpload<T>(
  url: string,
  filename: string,
  file: File,
  onProgress?: (percent: number) => void
): Promise<R<T>>

/**
 * 文件上传
 * @param url
 * @param filePath
 * @returns
 */
async function fileUpload<T>(
  url: string,
  formdata: FormData | string,
  file?: File,
  onProgress?: (percent: number) => void
): Promise<R<T>> {
  if (formdata instanceof FormData) {
    return fileUploadSend<T>(url, formdata, onProgress)
  }
  const form = new FormData()
  if (!file) {
    throw new Error('file不能为空')
  }
  form.append(formdata, file!, file!.name)
  return fileUploadSend<T>(url, form, onProgress)
}

async function fileUploadSend<T = any>(
  url: string,
  formdata: FormData,
  onProgress?: (percent: number) => void
): Promise<R<T>> {
  const fillUrl = getFillUrl(url)
  const [p, resolve, reject] = createResolvablePromise<R<T>>()
  const xhr = new XMLHttpRequest()
  if (onProgress) {
    xhr.upload.addEventListener('progress', (event) => {
      if (event.lengthComputable) {
        const percent = event.loaded / event.total
        onProgress(percent)
      }
    })
  }
  // 监听请求完成事件
  xhr.addEventListener('load', () => {
    if (xhr.status >= 200 && xhr.status < 300) {
      try {
        const response = JSON.parse(xhr.responseText)
        // console.log('上传成功，服务器返回:', response)
        resolve(new R(response))
      } catch (e) {
        console.error('解析响应失败:', e)
        console.log('原始响应:')
        reject(xhr.responseText)
      }
    } else {
      reject(xhr.status)
      console.error('上传失败，状态码:', xhr.status)
    }
  })
  xhr.addEventListener('error', () => {
    reject('上传失败')
  })

  // 初始化请求
  xhr.open('POST', fillUrl, true)
  xhr.setRequestHeader('token', getToken())
  xhr.send(formdata)
  return p
}

const request = {
  get,
  post,
  fileUpload,
  put,
  del,
  patch,
}

export class R<T> {
  /**
   * 状态码
   */
  code: number
  /**
   * 数据
   */
  data: T
  /**
   * 消息，code为200一般都是 'ok'
   */
  message: string

  originallyData: any

  constructor(data: any) {
    this.originallyData = data
    this.code = data.code
    this.data = data.data
    this.message = data.message
  }
  /**
   * 打开包装，如果code不为200，则弹出错误信息，并且抛出异常堵塞后续代码运行
   * @returns
   */
  unwrap() {
    const msg = this.message || JSON.stringify(this.originallyData)
    let showMsg = msg
    if (showMsg.length > 60) {
      showMsg = showMsg.substring(0, 60) + '...'
    }
    if (this.code !== 200) {
      if (showMsg == '非法访问') {
        showMsg = '登录已过期'
      }
      showToast(showMsg)
      throw new Error(msg)
    }
    return this.data
  }
  expection(msg: string) {
    if (this.code !== 200) {
      showToast(msg)
      throw new Error(this.message)
    }
    return this.data
  }
}

export default request
